use std::{net::IpAddr, sync::OnceLock};

use bytes::{Buf, Bytes, BytesMut};
use config::{get_session_name_bytes, sdp_config};
use field::{Connection, Media, MediaType, Origin, ProtoType, SessionName, Timing, Version};
use tools::{error::Error, ResultEx};
use tracing::trace;

use crate::{empty_sockaddr, Codec};

pub(crate) mod config;
pub(crate) mod field;

static SDP_BYTES: OnceLock<Sdp> = OnceLock::new();

///提供一个呼叫时的本地sdp信息.
///
/// 这个是当前固定可以交换的信息,因此与呼叫等无关.
pub fn local_sdp() -> Sdp {
	SDP_BYTES
		.get_or_init(|| {
			let m_vec = vec![Media::from_config(
				MediaType::Audio,
				ProtoType::RtpAvp,
				&sdp_config().rtp_audio_attribs,
			)];
			Sdp::new(
				Version::new(),
				Origin::new(empty_sockaddr().ip()),
				Connection::new(empty_sockaddr().ip()),
				Timing::new(),
				m_vec,
			)
		})
		.clone()
}

///提供一个呼叫时的sdp信息.
///
/// 这个是当前固定可以交换的信息,因此与呼叫等无关.
pub fn generate_sdp(addr: IpAddr, rtp_port: u16, rtcp_port: u16) -> Sdp {
	let mut sdp: Sdp = SDP_BYTES
		.get_or_init(|| {
			let m_vec = vec![Media::from_config(
				MediaType::Audio,
				ProtoType::RtpAvp,
				&sdp_config().rtp_audio_attribs,
			)];
			Sdp::new(
				Version::new(),
				Origin::new(addr),
				Connection::new(addr),
				Timing::new(),
				m_vec,
			)
		})
		.clone();

	sdp.set_addr(addr, rtp_port, rtcp_port);

	sdp
}

#[derive(Debug, Clone)]
pub struct Sdp {
	pub v: Version,
	pub o: Origin,
	pub s: SessionName,
	pub c: Connection,
	pub t: Timing,
	pub m: Vec<Media>,
	// pub i: SessionInfo,
	// pub u: SessionUri,
	// pub e: SessionEmail,
	// pub p: SessionPhone,

	// pub b: Bandwidth,
	// pub z: TimeZoneAdjustment,
	// pub k: Key,
	// pub a: Attribute,
	// pub r: RepeatTimes,
}

impl Codec for Sdp {
	fn write_to(&self, buf: &mut BytesMut) {
		self.v.write_to(buf);
		self.o.write_to(buf);
		self.s.write_to(buf);
		self.c.write_to(buf);
		self.t.write_to(buf);
		for m in self.m.iter() {
			m.write_to(buf);
		}
	}

	fn from_bytes(buf: &mut Bytes) -> ResultEx<Self> {
		if buf.is_empty() {
			return Err(Error::NotFound(""));
		}

		let mut v = None;
		let mut o = None;
		let mut c = None;
		let mut t = None;
		let mut m = Vec::with_capacity(10);
		while buf.len() > 2 {
			match &buf[..2] {
				b"v=" => v = Some(Version::from_bytes(buf)?),
				b"o=" => o = Some(Origin::from_bytes(buf)?),
				b"c=" => c = Some(Connection::from_bytes(buf)?),
				b"t=" => t = Some(Timing::from_bytes(buf)?),
				b"m=" => m.push(Media::from_bytes(buf)?),
				_ => jump_line(buf),
			}
		}

		if v.is_none() || o.is_none() || c.is_none() || t.is_none() || m.is_empty() {
			return Err(Error::CodeParseError(format!(
				"v:{v:?},o:{o:?},c:{c:?},t:{t:?},m:{m:?},"
			)));
		}

		let sdp = Sdp::new(v.unwrap(), o.unwrap(), c.unwrap(), t.unwrap(), m);
		trace!("开始解码sdp内容,消息:{sdp:?}");
		Ok(sdp)
	}
}

impl Sdp {
	///给出当前第一个媒体类型的rtp端口
	/// 没有返回0
	#[inline]
	pub fn first_rtp_port(&self) -> u16 {
		match self.m.get(0) {
			Some(p) => p.rtp_port,
			None => 0,
		}
	}

		///给出当前第一个媒体类型的rtp端口
	/// 没有返回0
	#[inline]
	pub fn first_rtcp_port(&self) -> u16 {
		match self.m.get(0) {
			Some(p) => p.rtcp_port,
			None => 0,
		}
	}
	///与另一个sdp进行合并操作,用以确定当前可用的一个sdp.
	/// 用来响应sdp请求,或者接收到请求以后用来确定自己的sdp的内容....
	pub fn merge(&self, other: &Self) -> ResultEx<Self> {
		let mut m_vec = Vec::with_capacity(self.m.len());
		for m in self.m.iter() {
			for o in other.m.iter() {
				if m.media_type == o.media_type {
					m_vec.push(m.merge(o));
				}
			}
		}

		let sdp = if m_vec.is_empty() {
			Err(Error::LogicalError("没有找到可用的媒体类型"))
		} else {
			Ok(Self::new(
				self.v.clone(),
				self.o.clone(),
				self.c.clone(),
				self.t.clone(),
				m_vec,
			))
		};

		trace!("merge两个sdp,新的sdp:{sdp:?}");

		sdp
	}

	pub fn new(v: Version, o: Origin, c: Connection, t: Timing, mut m: Vec<Media>) -> Self {
		//如果媒体里面没有连接地址,将上级的连接地址给他
		for item in m.iter_mut() {
			if !item.has_connect() {
				item.c = Some(c.clone());
			}
		}

		let s = SessionName::new(get_session_name_bytes().clone());

		Self { v, o, s, c, t, m }
	}

	pub fn set_addr(&mut self, ip: IpAddr, rtp_port: u16, rtcp_port: u16) {
		self.c.address = ip;
		self.o.address = ip;
		for item in self.m.iter_mut() {
			item.c = Some(self.c.clone());
			item.rtp_port = rtp_port;
			item.rtcp_port = rtcp_port;
		}
	}

	pub fn set_ip(&mut self, ip: IpAddr) {
		self.c.address = ip;
		self.o.address = ip;
		for item in self.m.iter_mut() {
			item.c = Some(self.c.clone());
		}
	}
}

///用来跳过一个不处理的行
#[inline]
fn jump_line(buf: &mut Bytes) {
	for i in 0..buf.len() {
		if buf[i] == b'\n' {
			buf.advance(i + 1);
			return;
		}
	}

	//没有了就直接给到最后
	buf.advance(buf.len())
}

#[test]
fn test_sdp() {
	let _log = tools::global_defin::trace_log::init_tracing_log();

	let mut buf =Bytes::from_static(b"v=0\r\no=FreeSWITCH 1724034739 1724034740 IN IP4 192.168.0.177\r\ns=FreeSWITCH\r\nc=IN IP4 192.168.0.177\r\nt=0 0\r\nm=audio 22258 RTP/AVP 102 9 0 8 105 101\r\na=rtpmap:102 opus/48000/2\r\na=fmtp:102 useinbandfec=1; maxaveragebitrate=30000; maxplaybackrate=48000; ptime=20; minptime=10; maxptime=40\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:105 telephone-event/48000\r\na=fmtp:105 0-15\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-15\r\na=ptime:20\r\nm=video 19776 RTP/AVP 103 104\r\nb=AS:3072\r\na=rtpmap:103 H264/90000\r\na=rtpmap:104 VP8/90000\r\na=rtcp-fb:103 ccm fir\r\na=rtcp-fb:103 ccm tmmbr\r\na=rtcp-fb:103 nack\r\na=rtcp-fb:103 nack pli\r\na=rtcp-fb:104 ccm fir\r\na=rtcp-fb:104 ccm tmmbr\r\na=rtcp-fb:104 nack\r\na=rtcp-fb:104 nack pli\r\n");

	let sdp = Sdp::from_bytes(&mut buf).unwrap();
	trace!("解析结果:{sdp:?}");

	let def_sdp = local_sdp();
	trace!("本地sdp:{def_sdp:?}");
	let me = sdp.merge(&def_sdp).unwrap();

	trace!("合并结果:{me:?}");
}
